<?
/*
	Класс для построения дерева.
	
	Создан: 2011-02-09
	Автор: djerom, tonatos
	Версия: 0.0.0.1
*/

	class c_tree_util
	{
		var $config = array('fid' => 'id', 'fpid' => 'pid');
	
		// Построить дерево по таблице. Связка по pid->id.
		function build($data)//, $callback
		{
			// Корень будующего дерева. В него помещаются узлы с pid=0 и узлы без родителя.
			// Сам корень данных не содержит (являются виртуальным узлом).
			$tree = array();
			
			$field_id = $this->config['fid'];
			$field_pid = $this->config['fpid'];
			
			// Создаем новый массив в котором ключами будут id записи. И заполняем его.
			$data2 = array();			
			foreach ($data as $k => $v)
			{
				$v['pid'] = $v[$field_pid];
				$data2[$v[$field_id]] = $v;				
			}
			$data = &$data2; // Теперь ключами в массиве являются id.
			
			// Пробегаем по всем записям и формируем из них дерево (путем направления ссылок в поле _childs)
			foreach ($data as $k => $v)
			{
				$pid = (int)$v[$field_pid]; // Получаем id родительской записи.
				
				// Если родитель существует, то добавим запись к нему в дети.
				if (isset($data[$pid]))
				{
					if (!isset($data[$pid]['_childs'])) $data[$pid]['_childs'] = array();
					$data[$pid]['_childs'][] = &$data[$k];
				}
				else
					$tree['_childs'][] = &$data[$k]; // Если нет, то поместим узел в корень.
			}

			return $tree;
		}

		// Пробежать по дереву, вызывая $callback для каждой записи.
		function walk(&$tree, $callback = 0, $addon = 0, $level = 0)
		{
			// Суть метода такова: Рекурсивно пробегаем по всем узлам дерева начиная с корня.
			// При каждом обращении к узлу вызываем $callback($item, $mode) 2 раза,
			// первый раз до обработки детей (mode=0), второй раз после (mode=1).
			// В callback передается ссылка на узел и шаблоны.
			
			$res = ""; // Собираем весь вывод callback сюда.
			
			$tree['_level'] = $level; // Заносим информацию об уровне.
			
			if ($callback) // Вызываем callback до обработки детей.
			{
				if (is_array($callback))
					$res .= call_user_method($callback[1], $callback[0], &$tree, 0, $addon);
				else
					$res .= $callback(&$tree, 0, $addon); // Просто функция.
			}
			
			// Рекурсивно обрабатываем детей, если они есть.
			if (isset($tree['_childs']))
			{
				foreach($tree['_childs'] as $k => $v)
				{
					$res .= $this->walk(&$tree['_childs'][$k], $callback, $addon, $level + 1);
				}
			}
			
			if ($callback) // Вызываем callback после обработки детей.
			{
				if (is_array($callback))
					$res .= call_user_method($callback[1], $callback[0], &$tree, 1, $addon);
				else
					$res .= $callback(&$tree, 1, $addon); // Просто функция.
			}


			return $res; // Вернем результат выводов callback.
		}
		
		function render(&$tree, $outer = '<p>|</p>', $inner = '<span>{name}</span>|')
		{
			if (!function_exists('render_item'))
			{
				function render_item($item, $mode, $data)
				{
					global $mvc;
					
					$level = (int)$item['_level'];
	
					$html = '';
					
					if ($mode == 0)
					{
						if ($level > 0)
							$html .= $mvc->parse($data['inner'][0], $item);// '<li>('.$item['id'].') ['.$item['pid'].'] - '.$item['pagetitle'];
	
						if (isset($item['_childs']))
							$html .= $mvc->parse($data['outer'][0], $item); //'<ul>';
					}
					else
					{
						if (isset($item['_childs']))
							$html .= $mvc->parse($data['outer'][1], $item); //'</ul>';
						
						if ($level > 0)
							$html .= $mvc->parse($data['inner'][1], $item); //'</li>';
					}
					
					return $html;
				}
			}
			
			$outer = explode('|', $outer);
			$inner = explode('|', $inner);
			$tpl = array('outer' => $outer, 'inner' => $inner);
			
			return $this->walk($tree, 'render_item', $tpl);	
		}		
	}
?>
